package com.rengwuxian.coursecoroutines._1_basics

import android.os.Bundle
import android.os.Handler
import android.os.Looper
import android.widget.TextView
import androidx.activity.ComponentActivity
import androidx.lifecycle.lifecycleScope
import com.rengwuxian.coursecoroutines.R
import com.rengwuxian.coursecoroutines.common.Contributor
import com.rengwuxian.coursecoroutines.common.gitHub
import io.reactivex.rxjava3.android.schedulers.AndroidSchedulers
import io.reactivex.rxjava3.disposables.Disposable
import kotlinx.coroutines.CoroutineScope
import kotlinx.coroutines.Dispatchers
import kotlinx.coroutines.Job
import kotlinx.coroutines.async
import kotlinx.coroutines.cancel
import kotlinx.coroutines.coroutineScope
import kotlinx.coroutines.delay
import kotlinx.coroutines.launch
import retrofit2.Call
import retrofit2.Callback
import retrofit2.Response
import java.util.concurrent.Executors

/**
 * 并行协程的启动和交互
 * 并行vs串行、async()/await()、join()
 *
 * 当然：所有协程都是并行的
 * 1.launch开启协程，这种是并行的
 *    lifecycleScope.launch {
 *    }
 *    lifecycleScope.launch {
 *    }
 *
 * 2.用挂起函数的写法，这种是串行的
 *   private fun singleCoroutineStyle() = lifecycleScope.launch {
 *     val contributors1 = gitHub.contributors("square", "retrofit")
 *     showContributors(contributors1)
 *   }
 *
 * 第2种写法可以交互，线程间可以拿到数据。
 *
 * 如果有两个并行的请求，然后结果进行合并，应该如何写呢？
 * 1.先并行，再串行。async，再 await
 *  val deferred = lifecycleScope.async {
 *    gitHub.contributors("square", "retrofit")
 *  }
 *  lifecycleScope.launch {
 *    val result1 = gitHub.contributors("square", "retrofit")
 *    val result2 = deferred.await()
 *    showContributors(result1 + result2)
 *  }
 *
 *  然后代码整洁的角度和结构化管理提供方便的角度，可以整理成：
 *  lifecycleScope.launch {
 *       coroutineScope {
 *         val deferred1 = async { gitHub.contributors("square", "retrofit") }
 *         val deferred2 = async { gitHub.contributors("square", "okhttp") }
 *         showContributors(deferred1.await() + deferred2.await())
 *       }
 *   }
 *
 *   Java的角度：用Java8的CompletableFuture，or用RxJava。
 *   private fun completableFutureStyleMerge() {
 *     val future1 = gitHub.contributorsFuture("square", "retrofit")
 *     val future2 = gitHub.contributorsFuture("square", "okhttp")
 *     future1.thenCombine(future2) { contributors1, contributors2 ->
 *       contributors1 + contributors2
 *     }.thenAccept { mergedContributors ->
 *       handler.post {
 *         showContributors(mergedContributors)
 *       }
 *     }
 *   }
 *
 *   如果两个并行任务，只在顺序上有依赖，而不依赖结果，可以使用join。和Thread的join很像。
 *   lifecycleScope.launch {
 *       val initJob = launch {
 * //        init()
 *       }
 *       val contributors1 = gitHub.contributors("square", "retrofit")
 *       initJob.join()
 * //      processData()
 *     }
 */
class ParallelActivity : ComponentActivity() {
  private val handler = Handler(Looper.getMainLooper())
  private lateinit var infoTextView: TextView

  override fun onCreate(savedInstanceState: Bundle?) {
    super.onCreate(savedInstanceState)
    setContentView(R.layout.layout_1)
    infoTextView = findViewById(R.id.infoTextView)

    lifecycleScope.launch {
      coroutineScope {
        val deferred1 = async { gitHub.contributors("square", "retrofit") }
        val deferred2 = async { gitHub.contributors("square", "okhttp") }
        showContributors(deferred1.await() + deferred2.await())
      }
    }
    lifecycleScope.launch {
      val initJob = launch {
//        init()
      }
      val contributors1 = gitHub.contributors("square", "retrofit")
      initJob.join()
//      processData()
    }
  }

  private fun singleCoroutineStyle() = lifecycleScope.launch {
    val contributors1 = gitHub.contributors("square", "retrofit")
    showContributors(contributors1)
  }

  private fun coroutinesStyle() = lifecycleScope.launch {
    val contributors1 = gitHub.contributors("square", "retrofit")
    val contributors2 = gitHub.contributors("square", "okhttp")
    showContributors(contributors1 + contributors2)
  }

  private fun completableFutureStyleMerge() {
    val future1 = gitHub.contributorsFuture("square", "retrofit")
    val future2 = gitHub.contributorsFuture("square", "okhttp")
    future1.thenCombine(future2) { contributors1, contributors2 ->
      contributors1 + contributors2
    }.thenAccept { mergedContributors ->
      handler.post {
        showContributors(mergedContributors)
      }
    }
  }

  private fun showContributors(contributors: List<Contributor>) = contributors
    .map { "${it.login} (${it.contributions})" }
    .reduce { acc, s -> "$acc\n$s" }
    .let { infoTextView.text = it }
}